﻿using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

namespace MapEditor
{

    public partial class Form1 : Form
    {
        Data info;
        ObjectDraw item;
        public static List<Image> UpperTextureList = new List<Image>();
        public static List<Image> LowerTextureList = new List<Image>();
        public bool isUpperLayerListSelected = true;

        //undo
        private List<GameObject>[] listUndo;
        private const int undo_num = 5;
        private int undo_index;
        private int countUndo;

        private FormAdv formAdvance;
        private FormMove formMove;

        public Form1()
        {
            formAdvance = new FormAdv(this);
            formMove = new FormMove(this);
            InitializeComponent();
        }
        private void Form1_Load(object sender, System.EventArgs e)
        {
            info = new Data();
            info.InitDefaultMap();
            LoadTexture();

            listUndo = new List<GameObject>[undo_num];
            for (int i = 0; i < undo_num; ++i)
                listUndo[i] = new List<GameObject>();

            tb_changeWidth.Text = Config.Map_W.ToString();
            tb_changeHeight.Text = Config.Map_H.ToString();

            panel_map.Width = Config.Map_W;
            panel_map.Height = Config.Map_H;
            panel_map.BackColor = Config.Color_BG;

            panel_base.Controls.Add(panel_map);

            listView.LargeImageList = imageList;
            listView_Lower.LargeImageList = imageList_Lower;

            for (int i = 0; i < imageList.Images.Count; i++)
            {
                ListViewItem lvi = new ListViewItem();
                lvi.ImageIndex = i;
                listView.Items.Add(lvi);

            }
            //MessageBox.Show(listView.Items.Count.ToString(), "");
            for (int i = 0; i < imageList_Lower.Images.Count; i++)
            {
                ListViewItem lvi = new ListViewItem();
                lvi.ImageIndex = i;
                listView_Lower.Items.Add(lvi);
            }

            InitDefaultItem_m();
        }

        private void tab_listview_SelectedIndexChanged(object sender, System.EventArgs e)
        {
            if (tab_listview.SelectedTab == tab_lv_upper)
                isUpperLayerListSelected = true;
            else isUpperLayerListSelected = false;
            CheckMap();
        }

        #region Change Map Dimension
        private void button_change_Click(object sender, System.EventArgs e)
        {
            changeDimension();
        }
        private void tb_changeDimension_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                changeDimension();
            }
        }
        #endregion

        #region Button
        private void button_advance_Click(object sender, System.EventArgs e)
        {
            formAdvance.ShowDialog();
        }
        private void button_move_Click(object sender, System.EventArgs e)
        {
            formMove.ShowDialog();
        }
        private void button_clear_Click(object sender, System.EventArgs e)
        {
            if (MessageBox.Show("Are you sure?\n(This action can't be undone)", "Clear all", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
            {
                info.InitDefaultMap();
                panel_map.Invalidate();
                countUndo = 0;
                undo_index = 0;
                foreach (List<GameObject> item in listUndo)
                    item.Clear();
                MessageBox.Show("Complete Clearing Map");
            }
        }
        private void button_clear_upper_Click(object sender, System.EventArgs e)
        {
            SetUndoList();
            info.Clear_UpperMap();
            panel_map.Invalidate();
            MessageBox.Show("Complete Clearing Upper Layer");
        }
        private void button_clear_lower_Click(object sender, System.EventArgs e)
        {
            SetUndoList();
            info.Clear_LowerMap();
            panel_map.Invalidate();
            MessageBox.Show("Complete Clearing Lower Layer");
        }
        private void button_map_change_Click(object sender, System.EventArgs e)
        {
            isUpperLayerListSelected = !isUpperLayerListSelected;
            CheckMap();
        }
        private void button_Save_Click(object sender, System.EventArgs e)
        {
            if (info.CountPlayer() == 0)
                MessageBox.Show("Please insert Mario");
            else
            {
                //Remove the objects outside the Map
                info.RemoveOutObjs(Config.Map_W, Config.Map_H);

                //QuadTree
                Rectangle boundWorld = info.GameBound();
                QuadTree quadTree = new QuadTree(boundWorld);
                quadTree.Build(info.ObjectList);
                //quadTree.SaveFileQuadTree("quadtree.txt", info.RealMapWidth);
                quadTree.SaveFileQuadTree("quadtree.txt");
                info.SaveObjectFile("object.txt");

                MessageBox.Show("Create Map Successfully");
            }
        }
        private void button_open_Click(object sender, System.EventArgs e)
        {
            OpenFileDialog openDialog = new OpenFileDialog();
            openDialog.Filter = "Mario Map(*.txt)|*.txt";
            DialogResult dlg = ShowDialogResult(openDialog);

            if (dlg == DialogResult.OK && openDialog.CheckPathExists)
            {
                info.LoadFile(openDialog.FileName);
                panel_map.Width = Config.Map_W;
                panel_map.Height = Config.Map_H;
                tb_changeWidth.Text = Config.Map_W.ToString();
                tb_changeHeight.Text = Config.Map_H.ToString();
                panel_map.Invalidate();
            }
            else
            {
                MessageBox.Show("File Not Found");
            }
        }
       
        #endregion

        #region Panel Events
        private void panel_map_MouseClick(object sender, MouseEventArgs e)
        {
            SetUndoList();
            Point pos = new Point((int)e.X, (int)e.Y);
            if (e.Button == MouseButtons.Left)
            {
                ApplyObj(pos);
            }
            else
            {
                info.ClearValue(pos, isUpperLayerListSelected);
                panel_map.Invalidate();
            }
        }

        //Check and add object 
        private bool ApplyObj(Point pos)
        {
            if (isUpperLayerListSelected)
            {
                if (listView.SelectedIndices[0] == 0 && info.CountPlayer() >= 1)
                    MessageBox.Show("Only one player in the game.");
                else
                {
                    //Keep Y-grid. To remove grid, change temp -> pos
                    //Point temp = info.ConvertLocation(pos);
                    if (info.ApplyValue(pos, listView.SelectedIndices[0], true))
                    {
                        item = new ObjectDraw(listView.SelectedIndices[0], panel_map, true);
                        item.Draw(pos);
                        panel_map.Invalidate();
                        return true;
                    }
                }
            }
            else
            {
                if (info.ApplyValue(pos, listView_Lower.SelectedIndices[0] + Config.ItemIndexSpace, false))
                {
                    item = new ObjectDraw(listView_Lower.SelectedIndices[0] + Config.ItemIndexSpace, panel_map, false);
                    item.Draw(pos);
                    panel_map.Invalidate();
                    return true;
                }
            }
            return false;
        }
        private void panel_map_Paint(object sender, PaintEventArgs e)
        {
            //DrawGrid();
            DrawMap_Lower();
            DrawMap_Upper();
        }
        #endregion

        #region Other forms
        public void AddAdvanceObject()
        {
            SetUndoList();
            Point location = formAdvance.StartDrawPosition;

            Size itemSize;
            if (isUpperLayerListSelected)
                itemSize = GameObject.ObjectDimension(listView.SelectedIndices[0]);
            else itemSize = GameObject.ObjectDimension(listView.SelectedIndices[0] + Config.ItemIndexSpace);

            //start drawing point, center point of the first item
            Point itemStartCenterPos = GameObject.ConvertToCenterPoint(location, itemSize);
            int num = formAdvance.NumberObj;
            bool drawInALine = formAdvance.DrawInALine;
            for (int i = 0; i < num; ++i)
            {
                if (drawInALine)
                {
                    if (!ApplyObj(new Point(itemStartCenterPos.X + itemSize.Width * i, itemStartCenterPos.Y)))
                        return;
                }
                else if (!ApplyObj(new Point(itemStartCenterPos.X, itemStartCenterPos.Y + itemSize.Height * i)))
                    return;
            }
        }
        public void MoveObject()
        {
            SetUndoList();
            Point location = formMove.Delta;

            //Expand map
            panel_map.Width += panel_map.Width + formMove.Delta.X;
            panel_map.Height += panel_map.Height + formMove.Delta.Y;

            Config.Map_H +=formMove.Delta.Y;
            Config.Map_W += formMove.Delta.X;

            tb_changeWidth.Text = Config.Map_W.ToString();
            tb_changeHeight.Text = Config.Map_H.ToString();

            foreach (GameObject item in info.ObjectList)
            {
                item.XUpper += location.X;
                item.YUpper += location.Y;
            }
            panel_map.Invalidate();
        }
        #endregion

        #region Methods
        private void LoadTexture()
        {
            string path = Directory.GetCurrentDirectory();
            //UpperTexture
            UpperTextureList.Add(Image.FromFile(path + @"\mario.png"));
            UpperTextureList.Add(Image.FromFile(path + @"\brick_normal.png"));
            UpperTextureList.Add(Image.FromFile(path + @"\brick_mr_red.png"));
            UpperTextureList.Add(Image.FromFile(path + @"\brick_mr_green.png"));
            UpperTextureList.Add(Image.FromFile(path + @"\brick_gold.png"));
            UpperTextureList.Add(Image.FromFile(path + @"\brick_hard.png"));
            UpperTextureList.Add(Image.FromFile(path + @"\groundgrass.png"));
            UpperTextureList.Add(Image.FromFile(path + @"\gomba.png"));
            UpperTextureList.Add(Image.FromFile(path + @"\turtle.png"));
            UpperTextureList.Add(Image.FromFile(path + @"\tubehead.png"));
            UpperTextureList.Add(Image.FromFile(path + @"\tubeBody.png"));

            //Lower Texture
            LowerTextureList.Add(Image.FromFile(path + @"\_tree.png"));
            LowerTextureList.Add(Image.FromFile(path + @"\_flower.png"));
            LowerTextureList.Add(Image.FromFile(path + @"\_cloud.png"));
        }

        private void DrawMap_Upper()
        {
            for (int i = 0; i < info.ObjectList.Count; i++)
            {
                if (info.ObjectList[i].IsUpperItem)
                {
                    item = new ObjectDraw(info.ObjectList[i].ItemIndex, panel_map, true);
                    item.Draw(info.ObjectList[i].CenterPosition);
                }
            }
        }
        private void DrawMap_Lower()
        {
            for (int i = 0; i < info.ObjectList.Count; i++)
            {
                if (!info.ObjectList[i].IsUpperItem)
                {
                    item = new ObjectDraw(info.ObjectList[i].ItemIndex, panel_map, false);
                    item.Draw(info.ObjectList[i].CenterPosition);
                }
            }
        }
        private void DrawGrid()
        {
            Graphics g = panel_map.CreateGraphics();
            for (int i = 0; i <= Config.Map_W / Config.Grid_Size.Width; i++)
                for (int j = 0; j <= Config.Map_H / Config.Grid_Size.Height; j++)
                {
                    g.DrawRectangle(new Pen(Config.Color_Grid), new Rectangle(new Point(i * Config.Grid_Size.Width, j * Config.Grid_Size.Height), new Size(Config.Grid_Size.Width, Config.Grid_Size.Height)));
                }
            g.Dispose();
        }

        private void CheckMap()
        {
            if (isUpperLayerListSelected)
            {
                if (tab_listview.SelectedTab != tab_lv_upper)
                {
                    tab_listview.SelectedTab = tab_lv_upper;
                    InitDefaultItem_m();
                }
            }
            else
            {
                if (tab_listview.SelectedTab != tab_lv_lower)
                {
                    tab_listview.SelectedTab = tab_lv_lower;
                    InitDefaultItem_lower();
                }
            }
        }

        private void InitDefaultItem_m()
        {
            listView.Items[0].Selected = true;
        }
        private void InitDefaultItem_lower()
        {
            listView_Lower.Items[0].Selected = true;
        }

        private void changeDimension()
        {
            int temp = int.Parse(tb_changeWidth.Text);
            //panel_m.Width = temp;
            panel_map.Width = temp;
            Config.Map_W = temp;

            temp = int.Parse(tb_changeHeight.Text);
            //panel_m.Height = temp;

            panel_map.Height = temp;
            Config.Map_H = temp;
        }

        private DialogResult ShowDialogResult(FileDialog dialog)
        {
            DialogState state = new DialogState();
            state.dialog = dialog;
            System.Threading.Thread thread = new System.Threading.Thread(state.ShowDialog);
            thread.SetApartmentState(System.Threading.ApartmentState.STA);
            thread.Start();
            thread.Join();
            return state.result;
        }

        //Display information (Label)
        private void panel_map_MouseMove(object sender, MouseEventArgs e)
        {
            Point mouseLocation = new Point(e.X, e.Y);

            //Show detais in label
            int id;
            string type;
            Point position;
            Size dimension;
            info.ShowDetail(mouseLocation, isUpperLayerListSelected,
                out id, out  type, out  position, out  dimension);

        }

        private void SetUndoList()
        {
            undo_index = (undo_index + 1) % undo_num;
            listUndo[undo_index].Clear();
            foreach (GameObject item in info.ObjectList)
            {
                listUndo[undo_index].Add(new GameObject(item));
            }
           
            if (countUndo < undo_num)
                ++countUndo;
        }
        
        #endregion
    }
}
